﻿using System.Buffers;
using System.Net;
using System.Net.Sockets;

namespace GCC.Container.Abstracts
{
    /// <summary>
    /// 附带具体类型的TCP通信客户端
    /// </summary>
    /// <typeparam name="T">你的通信数据类型</typeparam>
    /// <remarks>
    /// 这是一个channel封装,想自定义自己的实现继承后进行下列动作:
    /// 1.设置_flagsize为你的标志位大小
    /// 2.设置_headSize为你的头大小
    /// 3.如果flag解析改动了默认size  对应第一条,重写checkhead
    /// 4.如果头部解析改动了默认size  对应第二条,重写checkflag
    /// 5.实现那几个抽象函数
    /// </remarks>
    public abstract class AbsCommTcpClient<T> : AbsCommClient, IDisposable
    {
        /// <summary>
        /// 是否已经销毁
        /// </summary>
        protected bool _disposed;
        /// <summary>
        /// 原始套接字对象
        /// </summary>
        protected readonly Socket _socket;
        /// <summary>
        /// 目标服务器
        /// </summary>
        protected readonly IPEndPoint _target;

        /// <summary>
        /// 日志对象
        /// </summary>
        protected readonly AbsGCCLogger _logger;

        /// <summary>
        /// 头长度
        /// </summary>
        protected int _headSize = 4;

        /// <summary>
        /// 标识位长度
        /// </summary>
        protected int _flagSize = 2;

        /// <summary>
        /// 取消token
        /// </summary>
        protected readonly CancellationTokenSource _cancellToken = new();
        /// <summary>
        /// 当前服务的缓冲池
        /// </summary>
        protected readonly MemoryPool<byte> _memoryPool = MemoryPool<byte>.Shared;

        /// <summary>
        /// 远程地址
        /// </summary>
        public EndPoint? RemotePoint { get => _socket.RemoteEndPoint; }

        /// <summary>
        /// 远程地址
        /// </summary>
        public EndPoint? LocalPoint { get => _socket.LocalEndPoint; }

        /// <summary>
        /// 当获取到数据时事件
        /// </summary>
        public event Action<T>? GotData;



        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="targetIp">目标IP</param>
        /// <param name="logger">日志</param>
        /// <param name="name">名称</param>
        /// <param name="localInfo">本地地址信息</param>
        public AbsCommTcpClient(IPEndPoint targetIp, AbsGCCLogger logger, string name, IPEndPoint? localInfo)
        {
            ServerType = "TCP";
            _target = targetIp;
            _logger = logger;
            Name = name;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            if (localInfo != null)
            {
                _socket.Bind(localInfo);
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="targetIp">目标IP</param>
        /// <param name="logger">日志</param>
        /// <param name="name">名称</param>
        /// <param name="socket">对应通信对象</param>
        public AbsCommTcpClient(IPEndPoint targetIp, AbsGCCLogger logger, string name, Socket socket)
        {
            ServerType = "TCP";
            _target = targetIp;
            _logger = logger;
            Name = name;
            _socket = socket;

            HandleClient();
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        ~AbsCommTcpClient()
        {
            Dispose(false);
        }

        /// <summary>
        /// 销毁模型
        /// </summary>
        /// <param name="flag">是否是用户主动调用</param>
        protected virtual void Dispose(bool flag)
        {
            if (_disposed)
            {
                return;
            }
            _disposed = true;


            try
            {
                _cancellToken.Cancel();
                _cancellToken.Dispose();
                _memoryPool.Dispose();
                if (_socket.Connected)
                {
                    _socket.Shutdown(SocketShutdown.Both);
                }
                _socket.Dispose();
            }
            catch (Exception ex)
            {
                _logger.Error("AbsCommTcpClient dispose error.", exception: ex);
            }
        }

        /// <summary>
        /// 销毁模型
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 链接
        /// </summary>
        /// <returns></returns>
        public bool Connect()
        {
            if (_socket.Connected)
            {
                return true;
            }
            _socket.Connect(_target);
            HandleClient();
            return _socket.Connected;
        }

        /// <summary>
        /// 停止
        /// </summary>
        /// <returns></returns>
        public bool Stop()
        {
            _cancellToken.Cancel();
            _socket.Shutdown(SocketShutdown.Both);
            _socket.Close();
            return _socket.Connected;
        }


        /// <summary>
        /// 启动服务
        /// </summary>
        private void HandleClient()
        {
            _ = Task.Factory.StartNew(async () =>
            {
                Memory<byte> headBuffer = new byte[_headSize];
                Memory<byte>? flagBuffer = _flagSize == 0 ? null : new byte[_flagSize];
                var thisLoopReceived = 0;
                while (!_cancellToken.IsCancellationRequested && !_cancellToken.Token.IsCancellationRequested)
                {
                    try
                    {
                        thisLoopReceived = 0;
                        if (flagBuffer is not null)
                        {
                            var flag = await _socket.ReceiveAsync(flagBuffer.Value, SocketFlags.None, _cancellToken.Token);
                            if (!CheckFlag(flagBuffer.Value))
                            {
                                continue;
                            }
                        }

                        var res = _socket.ReceiveAsync(headBuffer, SocketFlags.None, _cancellToken.Token);
                        if (!CheckHeader(flagBuffer, headBuffer, out var bodyLength))
                        {
                            continue;
                        }
                        using var bodyBufferOwner = _memoryPool.Rent(bodyLength);
                        var bodyBuffer = bodyBufferOwner.Memory[..bodyLength];
                        while (thisLoopReceived < bodyLength)
                        {
                            thisLoopReceived += await _socket.ReceiveAsync(bodyBuffer, SocketFlags.None, _cancellToken.Token);
                        }
                        GotData?.Invoke(HandleData(flagBuffer, headBuffer, bodyBuffer));

                    }
                    catch (OperationCanceledException)
                    {
                        break;
                    }
                    catch (SocketException ex)
                    {
                        _logger.Critical($"Got socket exception, _socket {_socket.RemoteEndPoint} closed.", exception: ex);
                        break;
                    }
                    catch (ObjectDisposedException ex)
                    {
                        _logger.Warning($"Maybe you dispose this instance so fast than socket sutdown {_socket.RemoteEndPoint} closed.", exception: ex);
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.Error("Got ReceiveAsync data exception.", exception: ex);
                        continue;
                    }

                }
                if (!_disposed)
                {
                    try
                    {
                        _logger.Information($"Client {_socket.RemoteEndPoint} closed.");
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"Close _socket {_socket.RemoteEndPoint} got exception.", exception: ex);
                    }
                }
            }, _cancellToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
        }

        /// <summary>
        /// 检查标志位
        /// </summary>
        /// <param name="flagBuffer">标志位buffer</param>
        /// <returns></returns>
        protected abstract bool CheckFlag(in Memory<byte> flagBuffer);

        /// <summary>
        /// 检查头
        /// </summary>
        /// <param name="flagBuffer">标识位缓冲区</param>
        /// <param name="headBuffer">头部缓冲</param>
        /// <param name="bodyLength">内容长度</param>
        /// <returns></returns>
        protected abstract bool CheckHeader(in Memory<byte>? flagBuffer, in Memory<byte> headBuffer, out int bodyLength);

        /// <summary>
        /// 处理数据
        /// </summary>
        /// <param name="flagBuffer">标识位buffer</param>
        /// <param name="headBuffer">头部buffer</param>
        /// <param name="bodyBuffer">数据buffer</param>
        protected abstract T HandleData(in Memory<byte>? flagBuffer, in Memory<byte> headBuffer, in Memory<byte> bodyBuffer);

        /// <summary>
        /// 发送数据
        /// </summary> 
        /// <param name="data">数据</param>
        /// <returns></returns>
        public bool Send(T data)
        {
            try
            {
                int totalLen = _headSize + _flagSize + GetBodyLengh(data);
                using var allDataBufferOwner = _memoryPool.Rent(totalLen);
                var allDataBuffer = allDataBufferOwner.Memory[..totalLen];
                PutFlag(allDataBuffer[.._flagSize]);
                PutHeader(allDataBuffer[.._flagSize], allDataBuffer[_flagSize..(_flagSize + _headSize)], data);
                PutData(allDataBuffer[.._flagSize], allDataBuffer[_flagSize..(_flagSize + _headSize)], allDataBuffer[(_flagSize + _headSize)..], data);
                return _socket.Send(allDataBuffer.Span) == allDataBuffer.Length;
            }
            catch (Exception ex)
            {
                _logger.Error($"Client {Name} send data error", exception: ex);
                return false;
            }
        }

        /// <summary>
        /// 获取数据的内容长度
        /// </summary>
        /// <param name="data"></param>
        protected abstract int GetBodyLengh(T data);

        /// <summary>
        /// 检查标志位
        /// </summary>
        /// <param name="flagBuffer">标志位buffer</param>
        /// <returns></returns>
        protected abstract void PutFlag(Memory<byte> flagBuffer);

        /// <summary>
        /// 检查头
        /// </summary>
        /// <param name="flagBuffer">标识位缓冲区</param>
        /// <param name="headBuffer">头部缓冲</param> 
        /// <param name="data">数据</param>
        /// <returns></returns>
        protected abstract void PutHeader(in Memory<byte>? flagBuffer, Memory<byte> headBuffer, in T data);

        /// <summary>
        /// 处理数据
        /// </summary>
        /// <param name="flagBuffer">标识位buffer</param>
        /// <param name="headBuffer">头部buffer</param>
        /// <param name="bodyBuffer">数据buffer</param>
        /// <param name="data">数据</param>
        protected abstract void PutData(in Memory<byte>? flagBuffer, in Memory<byte> headBuffer, Memory<byte> bodyBuffer, in T data);

    }
}
